package com.ybg.engine;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.jdbc.core.JdbcTemplate;
import com.ybg.engine.task.Task;
import com.ybg.util.DataSourceUtil;
import com.ybg.util.LogUtil;
import com.ybg.util.SysParamUtil;

/**
 * 调度引擎加载后自启动类，主要是启动调度引擎，这是最重要的初始化类
 * 
 * @author zhouqixin
 */

public class EngineListener implements ApplicationListener<ApplicationEvent> {
	@Autowired
	@Qualifier("jdbcTemplate")
	private JdbcTemplate jdbcTemplate;
	private final String ControlJobCronExpress="*/5 * * * * ? *";//遍历调度任务5秒执行一次
	private static final String ControlJobName="com.ybg.engine.task.ControlJob";//遍历调度任务5秒执行一次

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ContextRefreshedEvent) {
			ContextRefreshedEvent tempEvent = (ContextRefreshedEvent) event;
			
			if (tempEvent.getApplicationContext().getParent() != null) {// root application context 没有parent，他就是老大.
				// 加载数据源，${catalina.home}/ybg/conf下所有的数据源文件
				DataSourceUtil.init(tempEvent.getApplicationContext());
				
				// 需要执行的逻辑代码，当spring容器初始化完成后就会执行该方法。
				JobEngineFactory.start();
				try {
					JobEngineFactory.startJob((Class<? extends Task>)(Class.forName(ControlJobName)), 
							ControlJobCronExpress);
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				traverseJobs(jdbcTemplate);
				
				// 初始化参数列表
				SysParamUtil.init(jdbcTemplate);
				
				
			}
		} else if (event instanceof ContextClosedEvent) {
			close();
		}

	}
	
	/**
	 * 遍历并处理所有的任务,-2停止，-1待停止，1待运行，2运行
	 */
	public static void traverseJobs(JdbcTemplate jdbcTemplate){
		// 数据库中的调度任务
		List<Map<String, Object>> taskList = jdbcTemplate.queryForList("select * from task_definition");
		
		// 工程内的调度任务
		List<Class<? extends Task>> subclasses = getSubClasses("com.ybg.engine.task");
		
		for (int i = 0; i < taskList.size(); i++) {
			boolean isExist = false;// 数据库中的调度任务在工程中是否存在
			
			for (int j = subclasses.size() - 1; j >= 0; j--) {
				if (subclasses.get(j).getName().equals(taskList.get(i).get("classname"))) {
					// 把合法并且是运行状态的调度任务加入到定时器中
					if ("2".equals(taskList.get(i).get("status"))) {
						JobEngineFactory.startJob(subclasses.get(j), taskList.get(i).get("cronexpress") + "");
					}
					subclasses.remove(j);
					isExist = true;
					break;
				}
			}
			
			if (!isExist) {// 数据库中定义的调度在工程中不存在时日志报错提醒
				LogUtil.error(taskList.get(i).get("classname") + " is not Exist in project or the task is non-standard!");
			}
		}

		// 工程中存在的调度在数据库中没有定义时日志警告提醒
		for (int i = 0; i < subclasses.size(); i++) {
			if(!ControlJobName.equals(subclasses.get(i).getName())){
				LogUtil.warn(subclasses.get(i).getName() + " is not Exist in table!");
			}
		}
	}
	

	/**
	 * 实例被销毁时调用，销毁实例时整个调度都需要关闭
	 */
	public void close() {
		// 关闭调度引擎
		JobEngineFactory.stop();
		
		//把待运行待停止全部置为运行停止
		jdbcTemplate.update("update task_definition set status='-1' where status='-2'");
		jdbcTemplate.update("update task_definition set status='1' where status='2'");
	}


	/**
	 * 获得指定包下的类
	 * 
	 * @param packageName
	 * @return
	 */
	public static List<Class<? extends Task>> getSubClasses(String packageName) {
		List<Class<?>> CLASSES = new ArrayList<Class<?>>(200);
		List<String> classesName = getClassName(packageName, true);
		
		for (int i = 0; i < classesName.size(); i++) {
			try {
				CLASSES.add(Class.forName(classesName.get(i)));
			} catch (ClassNotFoundException e) {
				LogUtil.error("不存在"+classesName.get(i)+"类", e);
			}
		}

		List<Class<? extends Task>> subClasses = new ArrayList<Class<? extends Task>>();
		
		for (Class<?> tmpClass : CLASSES) {
			if (Task.class.isAssignableFrom(tmpClass) && !tmpClass.isAssignableFrom(Task.class)) {
				subClasses.add((Class<? extends Task>) tmpClass);
			}
		}
		
		return subClasses;
	}

	/**
	 * 获取某包下所有类
	 * 
	 * @param packageName 包名
	 * @param childPackage 是否遍历子包
	 * @return 类的完整名称
	 */
	public static List<String> getClassName(String packageName, boolean childPackage) {
		List<String> fileNames = null;
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		String packagePath = packageName.replace(".", "/");
		URL url = loader.getResource(packagePath);
		if (url != null) {
			String type = url.getProtocol();
			if (type.equals("file")) {
				fileNames = getClassNameByFile(url.getPath(), null, childPackage);
			}
		}
		return fileNames;
	}

	/**
	 * 从项目文件获取某包下所有类
	 * 
	 * @param filePath 文件路径
	 * @param className 类名集合
	 * @param childPackage 是否遍历子包
	 * @return 类的完整名称
	 */
	private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
		List<String> myClassName = new ArrayList<String>();
		File file = new File(filePath);
		File[] childFiles = file.listFiles();
		for (File childFile : childFiles) {
			if (childFile.isDirectory()) {
				if (childPackage) {
					myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
				}
			} else {
				String childFilePath = childFile.getPath();
				if (childFilePath.endsWith(".class")) {
					childFilePath = childFilePath.substring(childFilePath.indexOf(File.separator+"classes") + 9, childFilePath.lastIndexOf("."));
					childFilePath = childFilePath.replace(File.separator, ".");
					myClassName.add(childFilePath);
				}
			}
		}

		return myClassName;
	}
}
